#include "FileDisplay.h"

//初始化颜色
void FileDisplay::initialize_colors()
{
    init_pair(1, COLOR_GREEN, -1);   // 关键字
    init_pair(2, COLOR_WHITE, -1);   // 字符串
    init_pair(3, COLOR_CYAN, -1);    // 注释
    init_pair(4, COLOR_MAGENTA, -1); // 数字
    init_pair(5, COLOR_BLUE, -1);    // 变量
    init_pair(6, COLOR_YELLOW, -1);  // 符号颜色
}

//读取文件
bool FileDisplay::load_file(const std::string &filename)
{
    std::ifstream file(filename);
    if (!file.is_open())
    {
        return false;
    }

    original_lines.clear();
    std::string line;
    while (std::getline(file, line))
    {
        original_lines.push_back(line);
    }
    file.close();
    return true;
}

// 分析语法高亮
void FileDisplay::analyze_syntax()
{
    // 清空之前的高亮信息
    highlight_info.clear();

    // 遍历每一行原始代码
    for (const auto &line : original_lines)
    {
        // 为当前行创建一个高亮信息向量，初始化为NORMAL类型
        std::vector<HighlightType> line_info(line.length(), HighlightType::NORMAL);
        
        // 标记是否在字符串中
        bool inString = false;
        // 标记是否在注释中
        bool inComment = false;
        // 标记是否遇到转义字符
        bool escapeChar = false;

        // 遍历当前行的每个字符
        for (size_t i = 0; i < line.length(); ++i)
        {
            // 如果已经在注释中，直接标记为COMMENT并继续
            if (inComment)
            {
                line_info[i] = HighlightType::COMMENT;
                continue;
            }

            // 如果遇到转义字符，重置标记并继续
            if (escapeChar)
            {
                escapeChar = false;
                continue;
            }

            // 检测到反斜杠，标记为转义字符
            if (line[i] == '\\')
            {
                escapeChar = true;
                continue;
            }

            // 检测到引号（单引号或双引号）
            if (line[i] == '"' || line[i] == '\'')
            {
                // 切换字符串状态
                inString = !inString;
                // 标记当前字符为STRING类型
                line_info[i] = HighlightType::STRING;
                continue;
            }

            // 如果不在字符串中且遇到#号，标记为注释开始
            if (!inString && line[i] == '#')
            {
                inComment = true;
                line_info[i] = HighlightType::COMMENT;
                continue;
            }

            // 如果在字符串中，标记为STRING类型
            if (inString)
            {
                line_info[i] = HighlightType::STRING;
            }
            // 如果是数字字符，标记为NUMBER类型
            else if (isdigit(line[i]))
            {
                line_info[i] = HighlightType::NUMBER;
            }
            // 如果遇到$符号，可能是变量
            else if (line[i] == '$')
            {
                // 标记$符号为VARIABLE类型
                line_info[i] = HighlightType::VARIABLE;
                
                // 继续标记变量名部分（字母数字或下划线）
                size_t j = i + 1;
                while (j < line.length() && (isalnum(line[j]) || line[j] == '_'))
                {
                    line_info[j] = HighlightType::VARIABLE;
                    j++;
                }
                // 跳过已处理的变量名部分
                i = j - 1;
            }
        }
        // 将当前行的高亮信息添加到结果中
        highlight_info.push_back(line_info);
    }

    // 第二遍处理：识别关键字
    for (size_t lineNum = 0; lineNum < original_lines.size(); ++lineNum)
    {
        const auto &line = original_lines[lineNum];
        auto &info = highlight_info[lineNum];

        size_t pos = 0;
        while (pos < line.length())
        {
            // 跳过空白字符和已标记的部分
            while (pos < line.length() && (isspace(line[pos]) || info[pos] != HighlightType::NORMAL))
            {
                pos++;
            }

            // 如果到达行尾则退出循环
            if (pos >= line.length())
                break;

            // 提取单词的开始位置
            size_t wordStart = pos;
            // 找到单词的结束位置
            while (pos < line.length() && !isspace(line[pos]) && info[pos] == HighlightType::NORMAL)
            {
                pos++;
            }

            // 获取完整的单词
            std::string word = line.substr(wordStart, pos - wordStart);
            // 检查是否是预定义的关键字
            if (SHELL_KEYWORDS.find(word) != SHELL_KEYWORDS.end())
            {
                // 如果是关键字，标记整个单词为KEYWORD类型
                for (size_t i = wordStart; i < wordStart + word.length(); ++i)
                {
                    info[i] = HighlightType::KEYWORD;
                }
            }
        }

        // 识别符号
        for (size_t i = 0; i < line.length(); ++i)
        {
            // 如果当前字符未被标记且是常见符号
            if (info[i] == HighlightType::NORMAL &&
                (line[i] == '=' || line[i] == '+' || line[i] == '-' ||
                 line[i] == '*' || line[i] == '/' || line[i] == '|' ||
                 line[i] == '&' || line[i] == '<' || line[i] == '>' ||
                 line[i] == '(' || line[i] == ')' || line[i] == '[' ||
                 line[i] == ']' || line[i] == '{' || line[i] == '}' ||
                 line[i] == ';' || line[i] == ':'))
            {
                // 标记为SYMBOL类型
                info[i] = HighlightType::SYMBOL;
            }
        }
    }
}

// 文件显示类的重新换行函数
void FileDisplay::rewrap_lines()
{
    // 清空当前存储的换行后内容
    wrapped_lines.clear();

    // 遍历原始文本的每一行
    for (size_t lineNum = 0; lineNum < original_lines.size(); ++lineNum)
    {
        // 获取当前行的文本内容
        const auto &line = original_lines[lineNum];
        // 获取当前行的高亮信息
        const auto &info = highlight_info[lineNum];

        // 如果当前行是空行，直接添加一个空行到换行结果中
        if (line.empty())
        {
            wrapped_lines.emplace_back("", std::vector<HighlightType>());
            continue;
        }

        // 初始化位置指针，用于记录当前处理到的字符位置
        size_t pos = 0;
        // 循环处理当前行的每个字符，直到处理完整行
        while (pos < line.length())
        {
            // 计算当前块的尺寸，取窗口宽度和剩余字符数的最小值
            int chunkSize = std::min((int)(line.length() - pos), win_width);
            // 将当前块的文本和高亮信息添加到换行结果中
            wrapped_lines.emplace_back(
                // 截取从pos开始，长度为chunkSize的子字符串
                line.substr(pos, chunkSize),
                // 截取对应位置的高亮信息
                std::vector<HighlightType>(info.begin() + pos, info.begin() + pos + chunkSize));
            // 移动位置指针到下一个块的起始位置
            pos += chunkSize;
        }
    }
}

// 刷新显示
// 刷新显示窗口内容的函数
void FileDisplay::refresh_display()
{
    // 清除窗口内容
    werase(win);
    
    // 计算当前窗口可以显示的行数，取窗口高度和剩余行数中的较小值
    int linesToShow = std::min(win_height, (int)wrapped_lines.size() - top_line);

    // 遍历每一行需要显示的内容
    for (int i = 0; i < linesToShow; ++i)
    {
        // 获取当前行的文本内容
        const auto &line = wrapped_lines[top_line + i].first;
        // 获取当前行的高亮信息
        const auto &info = wrapped_lines[top_line + i].second;

        // 遍历当前行的每一个字符
        for (size_t j = 0; j < line.length(); ++j)
        {
            // 如果当前字符没有高亮信息，直接输出
            if (j >= info.size())
            {
                waddch(win, line[j]);
                continue;
            }

            // 根据不同的高亮类型设置不同的颜色并输出字符
            switch (info[j])
            {
            case HighlightType::KEYWORD:  // 关键字类型
                wattron(win, COLOR_PAIR(1));  // 启用颜色对1
                waddch(win, line[j]);         // 输出字符
                wattroff(win, COLOR_PAIR(1));  // 关闭颜色对1
                break;
            case HighlightType::STRING:   // 字符串类型
                wattron(win, COLOR_PAIR(2));
                waddch(win, line[j]);
                wattroff(win, COLOR_PAIR(2));
                break;
            case HighlightType::COMMENT:  // 注释类型
                wattron(win, COLOR_PAIR(3));
                waddch(win, line[j]);
                wattroff(win, COLOR_PAIR(3));
                break;
            case HighlightType::NUMBER:   // 数字类型
                wattron(win, COLOR_PAIR(4));
                waddch(win, line[j]);
                wattroff(win, COLOR_PAIR(4));
                break;
            case HighlightType::VARIABLE: // 变量类型
                wattron(win, COLOR_PAIR(5));
                waddch(win, line[j]);
                wattroff(win, COLOR_PAIR(5));
                break;
            case HighlightType::SYMBOL:   // 符号类型
                wattron(win, COLOR_PAIR(6));
                waddch(win, line[j]);
                wattroff(win, COLOR_PAIR(6));
                break;
            default:  // 默认情况，直接输出字符
                waddch(win, line[j]);
                break;
            }
        }
        
        // 计算当前行剩余的空格数量
        int remaining = win_width - line.length();
        // 如果还有剩余空间，用空格填充
        if (remaining > 0)
        {
            wmove(win, i, line.length());  // 移动光标到行尾
            for (int k = 0; k < remaining; ++k)
            {
                waddch(win, ' ');  // 填充空格
            }
        }
    }

    // 如果窗口高度大于需要显示的行数，用空格填充剩余行
    for (int i = linesToShow; i < win_height - 1; ++i)
    {
        wmove(win, i, 0);  // 移动到每行开头
        for (int j = 0; j < win_width; ++j)
        {
            waddch(win, ' ');  // 填充空格
        }
    }

    // 显示状态信息（当前行/总行数）
    if (!wrapped_lines.empty())
    {
        // 构造状态字符串，格式为"当前行/总行数"
        std::string status = std::to_string(top_line + 1) + "/" +
                             std::to_string(wrapped_lines.size());
        // 在窗口右下角显示状态信息
        mvwaddstr(win, win_height - 1, win_width - status.length() - 1, status.c_str());
    }

    // 刷新窗口，使所有更改生效
    wrefresh(win);
}

// 处理输入
bool FileDisplay::handle_input(int ch)  // 处理用户输入的函数，参数ch表示输入的按键值
{
    refresh_display();  // 刷新显示内容

    switch (ch)  // 根据不同的按键值执行不同操作
    {
    case KEY_UP:  // 如果按下的是上箭头键
        if (top_line > 0)  // 检查当前顶部行号是否大于0（防止越界）
            top_line--;  // 将顶部行号减1（向上滚动一行）
        return true;  // 返回true表示已处理该输入

    case KEY_DOWN:  // 如果按下的是下箭头键
        if (top_line < (int)wrapped_lines.size() - win_height)  // 检查是否还有内容可以向下滚动
            top_line++;  // 将顶部行号加1（向下滚动一行）
        return true;  // 返回true表示已处理该输入

    case KEY_PPAGE:  // 如果按下的是Page Up键
        top_line = std::max(0, top_line - win_height);  // 向上滚动一页（win_height行），确保不会小于0
        return true;  // 返回true表示已处理该输入

    case KEY_NPAGE:  // 如果按下的是Page Down键
        top_line = std::min((int)wrapped_lines.size() - win_height, top_line + win_height);  // 向下滚动一页，确保不会超过最大行数
        return true;  // 返回true表示已处理该输入

    default:  // 其他按键情况
        return true;  // 默认返回true表示已处理该输入
    }
}